﻿
using Microsoft.Windows.Shell;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Security;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using UBCon.Core;
using UBCon.Core.Commands;
using UBCon.Resources;

namespace UBCon.Navigation
{

    //[System.Windows.TemplatePart(Name = PART_Content, Type = typeof(System.Windows.Controls.Border))]
    [TemplatePart(Name = ContentDecoratorName, Type = typeof(AdornerDecorator))]
    [TemplatePart(Name = PART_IconName, Type = typeof(Image))]
    [TemplatePart(Name = footerContentPresenterName, Type = typeof(FrameworkElement))]
    [TemplatePart(Name = rootPanelName, Type = typeof(Grid))]
    public partial class XWindow : Window
    {
        #region Constructors
        /// <summary>
        /// 
        /// </summary>
        static XWindow()
        {
            Type ownerType = typeof(XWindow);
            XWindow.DefaultStyleKeyProperty.OverrideMetadata(ownerType,
                 new FrameworkPropertyMetadata(ownerType)
                 );
        }

        /// <summary>
        /// 
        /// </summary>
        public XWindow()
            : base()
        {
            this.SetHook();
            this.RegisterCommands();
            this.IsBorderWidthMeasureValid = false;
            this.ResizeBorderWidth = 0.0D;
        }

        Point emtyPoint = new Point(0, 0);

        private void RibbonWindow_SizeChanged(object sender, SizeChangedEventArgs e)
        {
           
        }

        #endregion

        #region Properties

        public Brush FooterBackground
        {
            get { return (Brush)GetValue(FooterBackgroundProperty); }
            set { SetValue(FooterBackgroundProperty, value); }
        }

        // Using a DependencyProperty as the backing store for FooterBackground.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FooterBackgroundProperty =
            DependencyProperty.Register("FooterBackground", typeof(Brush), typeof(XWindow), new FrameworkPropertyMetadata(null));

        [Bindable(true),
        Browsable(false),
        ReadOnly(false)]
        public object Footer
        {
            get { return (object)GetValue(FooterProperty); }
            set { SetValue(FooterProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Footer.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty FooterProperty =
            DependencyProperty.Register("Footer", typeof(object), typeof(XWindow), new FrameworkPropertyMetadata(null));

        [Bindable(true),
        Browsable(true),
        ReadOnly(false),
        Category("Appearance")]
        public bool IsMinimizeButtonEnabled
        {
            get { return (bool)GetValue(IsMinimizeButtonEnabledProperty); }
            set { SetValue(IsMinimizeButtonEnabledProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMinimizeButtonEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMinimizeButtonEnabledProperty =
            DependencyProperty.Register("IsMinimizeButtonEnabled", typeof(bool), typeof(XWindow), new FrameworkPropertyMetadata(true));

        [Bindable(true),
        Browsable(true),
        ReadOnly(false),
        Category("Appearance")]
        public bool IsMaximizeButtonEnabled
        {
            get { return (bool)GetValue(IsMaximizeButtonEnabledProperty); }
            set { SetValue(IsMaximizeButtonEnabledProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsMaximizeButtonEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsMaximizeButtonEnabledProperty =
            DependencyProperty.Register("IsMaximizeButtonEnabled", typeof(bool), typeof(XWindow), new System.Windows.FrameworkPropertyMetadata(true));

        /// <summary>
        /// A boolean value indicating whether window has footer or not.
        /// </summary>
        [Bindable(true),
        Browsable(true),
        ReadOnly(false),
        Category("Appearance")]
        public bool HasFooter
        {
            get { return (bool)GetValue(HasFooterProperty); }
            set { SetValue(HasFooterProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HasFooter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HasFooterProperty =
            DependencyProperty.Register("HasFooter", typeof(bool), typeof(XWindow), new PropertyMetadata(true));

        public bool IsCloseButtonEnabled
        {
            get { return (bool)GetValue(IsCloseButtonEnabledProperty); }
            set { SetValue(IsCloseButtonEnabledProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsCloseButtonEnabled.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsCloseButtonEnabledProperty =
            DependencyProperty.Register("IsCloseButtonEnabled", typeof(bool), typeof(XWindow), new PropertyMetadata(true));

        internal Thickness MaximizeMargin
        {
            get { return (System.Windows.Thickness)GetValue(MaximizeMarginProperty); }
            set { SetValue(MaximizeMarginProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MaximizeMargin.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty MaximizeMarginProperty =
            DependencyProperty.Register("MaximizeMargin", typeof(System.Windows.Thickness), typeof(XWindow));

        #endregion

        #region Depenency Property Changed Callbacks


        #endregion

        #region Overrieds

        /// <summary>
        /// When overridden in a derived class, measures the size in layout required for child elements and determines a size for the FrameworkElement-derived class.
        /// </summary>
        /// <param name="availableSize">The available size that this element can give to child elements. Infinity can be specified as a value to indicate that the element will size to whatever content is available.</param>
        /// <returns>The size that this element determines it needs during layout, based on its calculations of child element sizes.</returns>
        protected override Size MeasureOverride(Size availableSize)
        {

            VerifyAccess();
            if (this.VisualChildrenCount > 0)
            {
                System.Windows.UIElement child = this.GetVisualChild(0) as System.Windows.UIElement;
                if (child != null)
                {
                    child.Measure(availableSize);
                    return child.DesiredSize;
                }
            }
            return base.MeasureOverride(availableSize);
        }

        /// <summary>
        /// When overridden in a derived class, is invoked whenever application code or internal processes call ApplyTemplate.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            this.mainBorder = this.GetTemplateChild(partOuterBorder) as FrameworkElement;
            this.titleText = this.GetTemplateChild(partTitleText) as TextBlock;
            
            if (titleText != null)
            {
                titleText.RenderTransform = tanslateTransform = new TranslateTransform();
            }

            this.ContentBorder = this.GetTemplateChild(PART_Content) as Border;
            
            this.icon = this.GetTemplateChild(PART_IconName) as Image;

            if (this.icon != null)
            {
                this.icon.MouseLeftButtonDown += new MouseButtonEventHandler(IconMouseLeftButtonDown);
                this.icon.MouseRightButtonDown += new MouseButtonEventHandler(IconMouseRightButtonDown);
            }

            this.cButPanel = this.GetTemplateChild("ButtonsPanel") as StackPanel;
            
            this.ContentDecorator = this.GetTemplateChild(ContentDecoratorName) as AdornerDecorator;
            
            FooterContentPresenter = GetTemplateChild(footerContentPresenterName) as FrameworkElement;
            
            rootPanel = GetTemplateChild(rootPanelName) as Grid;
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IconMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                if (SystemCommands.ShowSystemMenuCommand.CanExecute(null, this))
                {
                    SystemCommands.ShowSystemMenuCommand.Execute(null, this);
                }
            }
            else if (e.ClickCount == 2)
            {
                if (ApplicationCommands.Close.CanExecute(null, this))
                {
                    ApplicationCommands.Close.Execute(null, this);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IconMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (SystemCommands.ShowSystemMenuCommand.CanExecute(e, this))
            {
                SystemCommands.ShowSystemMenuCommand.Execute(e, this);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="arrangeBounds"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size arrangeBounds)
        {
            VerifyAccess();
            if (this.VisualChildrenCount > 0)
            {
                UIElement child = this.GetVisualChild(0) as UIElement;

                if (child != null)
                {
                    child.Arrange(new System.Windows.Rect(arrangeBounds));
                }
                return arrangeBounds;
            }
            return base.ArrangeOverride(arrangeBounds);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        #endregion

        #region Private Utilities

        internal void UpdateTitleLocation(Visibility visibility)
        {
            if (titleText == null)
            {
                return;
            }
            titleText.Visibility = visibility;
        }

        private Button ShowRibbonBarButton { get; set; }


        private void CalculateFramBorderWidth(System.IntPtr hwnd)
        {
            NativeMethods.RECT rect = new NativeMethods.RECT();
            NativeMethods.RECT rect1 = new NativeMethods.RECT();
            NativeMethods.GetWindowRect(hwnd, out rect);
            NativeMethods.GetClientRect(hwnd, out rect1);
            this.IsBorderWidthMeasureValid = true;
            this.ResizeBorderWidth = System.Math.Abs(rect.Width - rect1.Width) / 2;
            this.MaximizeMargin = new System.Windows.Thickness(this.ResizeBorderWidth);
        }

        /// <summary>
        /// Sets window procedure.
        /// </summary>
        private void SetHook()
        {
            System.EventHandler handler = delegate(object sender, System.EventArgs e)
            {
                HwndSource source = (HwndSource)System.Windows.PresentationSource.FromVisual(this);
                source.AddHook(new System.Windows.Interop.HwndSourceHook(this.Handler));
            };
            base.SourceInitialized += handler;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <param name="result"></param>
        [System.Security.SecurityCritical, System.Security.SecuritySafeCritical]
        private void WndProcHitTest(System.IntPtr hwnd, System.IntPtr lParam, ref bool handled, ref System.IntPtr result)
        {
            int xy = lParam.ToInt32();
            System.Windows.Point point = new System.Windows.Point(NativeMethods.SignedLoWord(xy), NativeMethods.SignedHiWord(xy));
            NativeMethods.RECT rect = new NativeMethods.RECT();
            NativeMethods.GetWindowRect(hwnd, out rect);
            System.Windows.Rect wndRect = new System.Windows.Rect(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
            NativeMethods.HT ht = this.NCHITTEST(point, wndRect);
            result = new System.IntPtr((int)ht);
            handled = ht != NativeMethods.HT.NOWHERE;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <param name="wndRect"></param>
        /// <returns></returns>
        [System.Security.SecurityCritical, System.Security.SecuritySafeCritical]
        private NativeMethods.HT NCHITTEST(System.Windows.Point point, System.Windows.Rect wndRect)
        {
            const double borderSize = 6.0;
            const double titleHeight = 34.0;

            if (point.Y < wndRect.Top + borderSize)
            {
                if (point.X < wndRect.Left + borderSize) return NativeMethods.HT.TOPLEFT;
                if (point.X > wndRect.Right - borderSize) return NativeMethods.HT.TOPRIGHT;
                return NativeMethods.HT.TOP;
            }
            if (point.Y > wndRect.Bottom - borderSize)
            {
                if (point.X < wndRect.Left + borderSize) return NativeMethods.HT.BOTTOMLEFT;
                if (point.X > wndRect.Right - borderSize) return NativeMethods.HT.BOTTOMRIGHT;
                return NativeMethods.HT.BOTTOM;
            }
            if (point.X < wndRect.Left + borderSize)
            {
                return NativeMethods.HT.LEFT;
            }
            if (point.X > wndRect.Right - borderSize) return NativeMethods.HT.RIGHT;
            if (point.Y < wndRect.Top + titleHeight)
            {
                System.Windows.Point localPoint = this.ScreenToLocal(point);
                System.Windows.IInputElement e = this.InputHitTest(localPoint);

                if (e == null)
                {
                    return NativeMethods.HT.CAPTION;
                }
                else
                {
                    if (e == mainBorder)
                    {

                        return NativeMethods.HT.CAPTION;
                    }

                    System.Windows.UIElement ue = e as System.Windows.UIElement;

                    if (ue == null ||
                            !ue.IsHitTestVisible ||
                                ue == this.titleText)
                    {
                        return NativeMethods.HT.CAPTION;
                    }
                }
            }
            return NativeMethods.HT.NOWHERE;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        [SecurityCritical, SecuritySafeCritical]
        private System.Windows.Point ScreenToLocal(System.Windows.Point point)
        {
            NativeMethods.RECT rect;
            NativeMethods.GetWindowRect(new System.Windows.Interop.WindowInteropHelper(this).Handle, out rect);
            System.Windows.Media.Matrix matrix = System.Windows.PresentationSource.FromVisual(this).CompositionTarget.TransformFromDevice;
            point.Offset((double)(-1 * rect.Left), (double)(-1 * rect.Top));
            point.X *= matrix.M11;
            point.Y *= matrix.M22;
            return point;
        }

        /// <summary>
        /// 
        /// </summary>
        [SecurityCritical, SecuritySafeCritical]
        private void RegisterCommands()
        {
            Type ownerType = typeof(XWindow);
            this.CommandBindings.Add(new System.Windows.Input.CommandBinding(MinimizeCommand, new System.Windows.Input.ExecutedRoutedEventHandler(this.PerformMinimize)));
            this.CommandBindings.Add(new System.Windows.Input.CommandBinding(MaximizeCommand, new System.Windows.Input.ExecutedRoutedEventHandler(this.PerformMaximize)));
            this.CommandBindings.Add(new System.Windows.Input.CommandBinding(CloseCommand, new System.Windows.Input.ExecutedRoutedEventHandler(this.PerformClose)));
            CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(SystemCommands.ShowSystemMenuCommand, SystemMenuExecuted, SystemMenuCanExecute));
        }

      
        #endregion

        #region Event Handlers

        private static void SystemMenuCanExecute(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = true;
        }

        private static void SystemMenuExecuted(object sender, ExecutedRoutedEventArgs args)
        {
            XWindow rw = sender as XWindow;
            if (rw != null)
            {
                // For right-clicks, display the system menu from the point of the mouse click.
                // For left-clicks, display the system menu in the top-left corner of the client area.
                Point devicePoint;
                MouseButtonEventArgs e = args.Parameter as MouseButtonEventArgs;
                if (e != null)
                {
                    // This is the right-click handler.  The presence of a MouseButtonEventArgs as args.Parameter
                    // indicates we are handling right-click.
                    devicePoint = rw.PointToScreen(e.GetPosition(rw));
                }
                else if (rw.mainBorder != null)
                {
                    // This is the left-click handler.  We can only handle it correctly if the _clientAreaBorder
                    // template part is defined, because that is where we want to position the system menu.
                    devicePoint = rw.mainBorder.PointToScreen(new Point(0, 0));
                }
                else
                {
                    // We can't handle this correctly, so exit.
                    return;
                }

                CompositionTarget compositionTarget = PresentationSource.FromVisual(rw).CompositionTarget;
                SystemCommands.ShowSystemMenu(rw, compositionTarget.TransformFromDevice.Transform(devicePoint));
                args.Handled = true;
            }
        }

        /// <summary>
        /// Main window procedure WndProc
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <param name="handled"></param>
        /// <returns></returns>
        [SecurityCritical, SecuritySafeCritical]
        protected virtual System.IntPtr Handler(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)
        {
            System.IntPtr result = System.IntPtr.Zero;

            NativeMethods.WindowMessages wm = (NativeMethods.WindowMessages)msg;

            if (System.Environment.OSVersion.Version.Major >= 6)
            {
                handled = NativeMethods.DwmDefWindowProc(hwnd, msg, wParam, lParam, out result);
            }

            if (!handled)
            {
                switch (wm)
                {
                    case NativeMethods.WindowMessages.WM_NCCALCSIZE:
                        if (!this.IsBorderWidthMeasureValid)
                        {
                            this.CalculateFramBorderWidth(hwnd);
                        }
                        handled = true;
                        result = System.IntPtr.Zero;
                        break;

                    case NativeMethods.WindowMessages.WM_SIZE:
                        handled = false;
                        break;
                    case NativeMethods.WindowMessages.WM_SETICON:
                        handled = true;
                        return System.IntPtr.Zero;
                    case NativeMethods.WindowMessages.WM_SETTEXT:
                        handled = true;
                        this.InvalidateArrange();
                        break;

                    case NativeMethods.WindowMessages.WM_NCHITTEST:
                        if (result == System.IntPtr.Zero)
                        {
                            this.WndProcHitTest(hwnd, lParam, ref handled, ref result);
                        }
                        break;
                }
            }

            return result;
        }

        #endregion

        #region Fields
        private const string partOuterBorder = "PART_OuterBorder";
        private const string partTitleText = "PART_TitleText";
        private const string PART_Content = "PART_Content";
        private const string PART_IconName = "PART_Icon";

        private System.Windows.FrameworkElement mainBorder;
        TextBlock titleText;
        public System.Windows.Controls.Border ContentBorder { get; set; }
        //  private RibbonApplicationMenu ApplicationMenu { get; set; }
        //private System.Windows.Point CurrentLocation = new System.Windows.Point();
        internal System.Windows.Controls.Image icon;
        private System.Windows.Controls.StackPanel cButPanel;
        private const string ContentDecoratorName = "PART_ContentAdorner";
        private const string propertyPath = "(UIElement.RenderTransform).(TranslateTransform.X)";
        private const string footerContentPresenterName = "PART_Footer";
        private AdornerDecorator ContentDecorator { get; set; }
        internal FrameworkElement FooterContentPresenter { get; set; }
        private TranslateTransform tanslateTransform = null;

        #endregion

        #region Internal Properties

        private bool IsBorderWidthMeasureValid { get; set; }

        public double ResizeBorderWidth { get; set; }

        /// <summary>
        /// 
        /// </summary>
        internal UIElement RootControl
        {
            get
            {
                System.Windows.UIElement child = null;

                if (this.VisualChildrenCount > 0)
                {
                    child = this.GetVisualChild(0) as System.Windows.UIElement;
                }
                return child;
            }
        }

        private bool IsFooterPresenterAvaliable
        {
            get
            {
                return FooterContentPresenter != null;
            }
        }

        /// </summary>
        private Storyboard HideFooterAnimation { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private Storyboard ShowFooterAnimation { get; set; }

        /// <summary>
        /// Root panel of window.
        /// </summary>
        private Grid rootPanel;

        private const string rootPanelName = "PART_RootPanel";

        private bool IsValidRootPanel
        {
            get { return rootPanel != null; }
        }
        #endregion

        #region Commands
        private static System.Windows.Input.RoutedUICommand closeCommand = new System.Windows.Input.RoutedUICommand("Close", "CloseCommand", typeof(XWindow));

        public static System.Windows.Input.RoutedUICommand CloseCommand
        {
            get
            {
                return closeCommand;
            }
        }

        private static System.Windows.Input.RoutedUICommand minimizeCommand = new System.Windows.Input.RoutedUICommand("Minimize", "MinimizeCommand", typeof(XWindow));

        public static System.Windows.Input.RoutedUICommand MinimizeCommand
        {
            get { return minimizeCommand; }
        }

        private static System.Windows.Input.RoutedUICommand maximizeCommand = new System.Windows.Input.RoutedUICommand("Maximize", "MaximizeCommand", typeof(XWindow));

        public static System.Windows.Input.RoutedUICommand MaximizeCommand
        {
            get { return maximizeCommand; }
        }

        private void PerformClose(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
        {
            e.Handled = true;
            this.Close();
        }

        private void PerformMinimize(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
        {
            e.Handled = true;

            if (this.WindowState != System.Windows.WindowState.Minimized)
                this.WindowState = System.Windows.WindowState.Minimized;
        }

        //private double pTop = 0.0, pLeft = 0.0, pWidth=0.0, pHeight=0;

        private void PerformMaximize(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
        {
            e.Handled = true;

            this.WindowState = this.WindowState != System.Windows.WindowState.Maximized ? System.Windows.WindowState.Maximized : System.Windows.WindowState.Normal;

        }

        #endregion

    }
}
